#pragma once

#include "../im.h"
#include "mysql/mysql.h"

class QueryResult
{
public:
    typedef std::map<std::string, uint32_t> FieldNames;

    QueryResult(MYSQL_RES *result, uint64_t rowCount, uint32_t fieldCount);
    virtual ~QueryResult();

    int getField_idx(const std::string &name) const
    {
        auto it = m_FieldNames.find(name);
        if (it != m_FieldNames.end())
        {
            return it->second;
        }
        return -1;
    }

    const char *operator[](int index) const
    {
        if (index < 0 || index >= m_FieldCount)
        {
            LOG_W << "QueryResult::operator[]: " << index << " out of range";
            return nullptr;
        }
        return m_CurrentRow[index];
    }

    const char *operator[](const std::string &name) const
    {
        auto it = m_FieldNames.find(name);
        if (it != m_FieldNames.end())
        {
            return m_CurrentRow[it->second];
        }
        LOG_W << "QueryResult::operator[]: " << name << " not found";
        return nullptr;
    }

    inline const std::string ToString(const std::string &str) const
    {
        auto index = getField_idx(str);
        if (index < 0 || index >= m_FieldCount)
            return std::string();

        auto cha = m_CurrentRow[index];
        return cha ? std::string(cha) : std::string();
    }

    inline const float ToFloat(const std::string &ostr) const
    {
        auto str = (*this)[ostr];
        return str ? static_cast<float>(atof(str)) : 0.0f;
    }

    inline const double ToDouble(const std::string &ostr) const
    {
        auto str = (*this)[ostr];
        return str ? atof(str) : 0.0;
    }

    inline const int8_t ToInt8(const std::string &ostr) const
    {
        auto str = (*this)[ostr];
        return str ? static_cast<int8_t>(atoi(str)) : 0;
    }

    inline const uint8_t ToUInt8(const std::string &ostr) const
    {
        auto str = (*this)[ostr];
        return str ? static_cast<uint8_t>(atoi(str)) : 0;
    }

    inline const int16_t ToInt16(const std::string &ostr) const
    {
        auto str = (*this)[ostr];
        return str ? static_cast<int16_t>(atoi(str)) : 0;
    }

    inline const uint16_t ToUInt16(const std::string &ostr) const
    {
        auto str = (*this)[ostr];
        return str ? static_cast<uint16_t>(atol(str)) : 0;
    }

    inline const int32_t ToInt32(const std::string &ostr) const
    {
        auto str = (*this)[ostr];
        return str ? static_cast<int32_t>(atol(str)) : 0;
    }

    inline const uint32_t ToUInt32(const std::string &ostr) const
    {
        auto str = (*this)[ostr];
        return str ? static_cast<uint32_t>(atol(str)) : 0;
    }

    inline const int64_t ToInt64(const std::string &ostr) const
    {
        auto str = (*this)[ostr];
        return str ? static_cast<int64_t>(atoll(str)) : 0;
    }

    inline const uint64_t ToUInt64(const std::string &ostr) const
    {
        auto str = (*this)[ostr];
        return str ? static_cast<uint64_t>(atoll(str)) : 0;
    }

    inline const bool ToBool(const std::string &ostr) const
    {
        auto str = (*this)[ostr];
        return str ? (atoi(str) != 0) : false;
    }

    bool nextRow();

    bool isEnd() const { return !m_CurrentRow; }

protected:
    uint32_t m_FieldCount;
    uint64_t m_RowCount;
    FieldNames m_FieldNames;

    MYSQL_RES *m_Result;
    MYSQL_ROW m_CurrentRow;

    unsigned long int *m_Lengths;

public:
    uint64_t getRowCount() const { return m_RowCount; }
};

typedef std::shared_ptr<QueryResult> QueryResultPtr;